namespace welog.Utilities;

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;




public class KeyboardHook
    {
        #region 常数和结构
        #region wParam对应的按钮事件
        public const int WM_KEYDOWN = 0x100;    // 键盘被按下
        public const int WM_KEYUP = 0x101;      // 键盘被松开
        public const int WM_SYSKEYDOWN = 0x104; // 键盘被按下，这个是系统键被按下，例如Alt、Ctrl等键
        public const int WM_SYSKEYUP = 0x105;   // 键盘被松开，这个是系统键被松开，例如Alt、Ctrl等键
        #endregion
        public const int WH_KEYBOARD_LL = 13;
        
        
        // 快捷键消息的ID
        private const int WM_HOTKEY = 0x0312;
        // 注册快捷键的ID
        private static int hotKeyId = 1;
 
        [DllImport("user32.dll", SetLastError = true)]
        private static extern bool RegisterHotKey(IntPtr hWnd, int id, KeyModifiers fsModifiers, Keys vk);

        [StructLayout(LayoutKind.Sequential)] //声明键盘钩子的封送结构类型 
        public class KeyboardHookStruct

        {
            public int vkCode; //表示一个在1到254间的虚似键盘码 
            public int scanCode; //表示硬件扫描码 
            public int flags;
            public int time;
            public int dwExtraInfo;
        }
        #endregion

        #region 成员变量、委托、事件
        private static int hHook;
        private static HookProc KeyboardHookDelegate;
        // 键盘回调委托
        public delegate void KeyboardHandler(Int32 wParam, KeyboardHookStruct keyboardHookStruct);
        // 键盘回调事件
        private static event KeyboardHandler Handlers;
        // 锁
        private readonly object lockObject = new object();
        // 当前状态,是否已经启动
        private volatile bool isStart = false;
        #endregion

        #region Win32的Api
        private delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);
        //安装钩子的函数 
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        private static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);

        //卸下钩子的函数 
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        private static extern bool UnhookWindowsHookEx(int idHook);

        //下一个钩挂的函数 
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        private static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        private static extern IntPtr GetModuleHandle(string lpModuleName);
        
        //如果函数执行成功，返回值不为0。
        //如果函数执行失败，返回值为0。要得到扩展错误信息，调用GetLastError。
        [DllImport("user32.dll", SetLastError = true)]
        public static extern bool RegisterHotKey(
            IntPtr hWnd,        //要定义热键的窗口的句柄
            int id,           //定义热键ID（不能与其它ID重复）      
            KeyModifiers fsModifiers,  //标识热键是否在按Alt、Ctrl、Shift、Windows等键时才会生效
            int vk           //定义热键的内容
        );
        [DllImport("user32.dll", SetLastError = true)]
        public static extern bool UnregisterHotKey(
            IntPtr hWnd,        //要取消热键的窗口的句柄
            int id           //要取消热键的ID
        );
        
        [DllImport("User32.dll",EntryPoint="FindWindow")]
        private static extern IntPtr FindWindow(string lpClassName,string lpWindowName);
        
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool GetMessage(ref Message msg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax);
        
        #endregion

        #region 单例模式来获取对象
        private static volatile KeyboardHook MyKeyboard;
        private readonly static object createLock = new object();
        private KeyboardHook() { }
        public static KeyboardHook GetKeyboardHook()
        {
            if (MyKeyboard == null)
            {
                lock (createLock)
                {
                    if (MyKeyboard == null)
                    {
                        MyKeyboard = new KeyboardHook();
                    }
                }
            }
            return MyKeyboard;
        }
        #endregion

        /// <summary>
        /// 安装钩子
        /// </summary>
        public void Start()
        {
            if (isStart)
            {
                return;
            }
            lock (lockObject)
            {
                if (isStart)
                {
                    return;
                }
                if (Handlers == null)
                {
                    throw new Exception("Please set handler first!Then run Start");
                }
                KeyboardHookDelegate = new HookProc(KeyboardHookProc);
                Process cProcess = Process.GetCurrentProcess();
                ProcessModule cModule = cProcess.MainModule;
                var mh = GetModuleHandle(cModule.ModuleName);
                hHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookDelegate, mh, 0);
                isStart = true;
            }
        }

        public IntPtr FindWindowByName(string lpWindowName)
        {
            return FindWindow(null, lpWindowName);
        }

        /// <summary>
        /// 卸载钩子
        /// </summary>
        public void Stop()
        {
            if (!isStart)
            {
                return;
            }
            lock (lockObject)
            {
                if (!isStart)
                {
                    return;
                }
                UnhookWindowsHookEx(hHook);
                // 清除所有事件
                Handlers = null;
                isStart = false;
            }
        }

        /// <summary>
        /// 键盘的系统回调函数
        /// </summary>
        /// <param name="nCode"></param>
        /// <param name="wParam"></param>
        /// <param name="lParam"></param>
        /// <returns></returns>
        private static int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
        {
            //如果该消息被丢弃（nCode<0）或者没有事件绑定处理程序则不会触发事件
            if ((nCode >= 0) && Handlers != null)
            {
                KeyboardHookStruct KeyDataFromHook = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
                Handlers(wParam, KeyDataFromHook);
            }
            return CallNextHookEx(hHook, nCode, wParam, lParam);
        }

        /// <summary>
        /// 添加按键的回调函数
        /// </summary>
        /// <param name="handler"></param>
        public void AddKeyboardHandler(KeyboardHandler handler)
        {
            Handlers += handler;
        }

        /// <summary>
        /// 删除指定按键的回调函数
        /// </summary>
        /// <param name="handler"></param>
        public void RemoveKeyboardHandler(KeyboardHandler handler)
        {
            if (Handlers != null)
            {
                Handlers -= handler;
            }
        }
    }
    
